#include "depui/depui.h"

#define MX__SLIDER_SIZE 10

void mx__slider_set(MX_SLIDER_DATA * slider, int range, int size, int value)
{
	int highest;

	MXDEBUG_INVARIANT(MXOBJ(slider));

		/** If the range, size or value is MXDEFAULT then the previous value
        is used. */
	if (range != MXDEFAULT)
		slider->_range = range;
	if (size != MXDEFAULT)
		slider->_size = size;
	if (value != MXDEFAULT)
		slider->_value = value;

	highest = slider->_range - slider->_size;

	if (slider->_value > highest)
		slider->_value = highest;
	if (slider->_value < 0)
		slider->_value = 0;

	slider->_upper = 0;
	slider->_lower = slider->_dim;

	if (slider->_range) {
		const int _range = slider->_dim - slider->_min;
		const int _thumb = ((long) slider->_size * _range) / slider->_range;
		const int _index = ((long) slider->_value * _range) / slider->_range;

		slider->_upper = _index;
		slider->_lower = slider->_upper + _thumb + slider->_min + 1;
	}
}

void mx__slider_to(MX_SLIDER_DATA * slider, int value)
{
	const int oldvalue = slider->_value;

	MXDEBUG_INVARIANT(MXOBJ(slider));

	mx__slider_set(slider, MXDEFAULT, MXDEFAULT, value);

	/** If the slider position is changed then a MX_HSCROLL or a MX_VSCROLL
        event is passed to the sliders parent. */
	if (slider->_value != oldvalue) {

		if (slider->_dim == mx_w(slider)) {
			mx_emit(MX_HSCROLL, 0, 0);

		} else if (slider->_dim == mx_h(slider)) {
			mx_emit(MX_VSCROLL, 0, 0);

		} else {
			assert(0);
		}

		mx_dirty(slider, true);
	}
}

int mx__slider_value(const MX_SLIDER_DATA * slider)
{
	MXDEBUG_INVARIANT(MXOBJ(slider));

	return slider->_value;
}

void mx_vslider_class(void)
{
	MX_SLIDER *s = (MX_SLIDER *) mx.obj;
	MX_SLIDER_DATA *slider = MXSLIDER(s);

	MXDEBUG_CLASSINVARIANT(MXOBJ(s));

	switch (mx.event) {

	case MX_EXPOSE:
		mx__theme->vslider(s);
		return;

	case MX_POINTER_PRESS:
		{
			const MX_POINTER_INFO *info = mx_pointer_info();

			if (info->y < mx_y1(slider) + slider->_upper) {
				mx__slider_to(slider, slider->_value - slider->_size);
				return;

			} else if (info->y > mx_y1(slider) + slider->_lower) {
				mx__slider_to(slider, slider->_value + slider->_size);
				return;
			}

			mx_dirty(slider, true);
			mx_wantmove(slider, true);
			mx_pointer_hold();
			break;

		}
	case MX_POINTER_RELEASE:
		mx_dirty(slider, true);
		mx_wantmove(slider, false);
		mx_pointer_release();
		break;

	case MX_POINTER_MOVE:
		if (mx_armed(slider)) {
			const MX_POINTER_INFO *info = mx_pointer_info();

			if ((info->dy) && (slider->_range)) {
				const int _range = mx_h(slider) - 2 - slider->_min;
				const int dy = (long) info->dy * slider->_range / _range;

				mx__slider_to(slider, slider->_value + dy);
			}
		}
		break;

	case MX_GEOMETRY:
		slider->_dim = mx_h(slider);
		mx__slider_set(slider, MXDEFAULT, MXDEFAULT, MXDEFAULT);
		return;

	case MX_DEFAULTRECT:
		{
			MX_RECT *rrect = mx_defaultrect_data();

			rrect->x2 = rrect->x1 + MX__SLIDER_SIZE;
			return;
		}

	default:
		break;
	}
	mx_obj_class();
}

MX_SLIDER *mx__vslider(MX_SLIDER_DATA * slider, size_t size, MX_OBJ_DATA * parent, int theid)
{
	MXMINSIZE(size, MX_SLIDER);

	slider = mx__obj(MXOBJ(slider), mx_vslider_class, size, parent, theid);
	if (slider) {
		MXDEBUG_ALLOCTAG(slider);
		MXDEBUG_ATOMNAME(slider, "vslider");
		slider->_min = 5;

		mx_wantpointer(slider, true);
	}
	MXDEBUG_INVARIANT(MXOBJ(slider));
	return (MX_SLIDER *) slider;
}

void mx_hslider_class(void)
{
	MX_SLIDER *s = (MX_SLIDER *) mx.obj;
	MX_SLIDER_DATA *slider = MXSLIDER(s);

	MXDEBUG_CLASSINVARIANT(MXOBJ(s));

	switch (mx.event) {

	case MX_EXPOSE:
		mx__theme->hslider(s);
		return;

	case MX_POINTER_PRESS:
		{
			const MX_POINTER_INFO *info = mx_pointer_info();

			if (info->x < mx_x1(slider) + slider->_upper) {
				mx__slider_to(slider, slider->_value - slider->_size);
				return;

			} else if (info->x > mx_x1(slider) + slider->_lower) {
				mx__slider_to(slider, slider->_value + slider->_size);
				return;
			}

			mx_dirty(slider, true);
			mx_wantmove(slider, true);
			mx_pointer_hold();
			break;

		}
	case MX_POINTER_RELEASE:
		mx_dirty(slider, true);
		mx_wantmove(slider, false);
		mx_pointer_release();
		break;

	case MX_POINTER_MOVE:
		if (mx_armed(slider)) {
			const MX_POINTER_INFO *info = mx_pointer_info();

			if ((info->dx) && (slider->_range)) {
				const int _range = mx_w(slider) - 2 - slider->_min;
				const int dx = (long) info->dx * slider->_range / _range;

				mx__slider_to(slider, slider->_value + dx);
			}
		}
		break;

	case MX_GEOMETRY:
		slider->_dim = mx_w(slider);
		mx__slider_set(slider, MXDEFAULT, MXDEFAULT, MXDEFAULT);
		return;

	case MX_DEFAULTRECT:
		{
			MX_RECT *rrect = mx_defaultrect_data();

			rrect->y2 = rrect->y1 + MX__SLIDER_SIZE;
			return;
		}

	default:
		break;
	}
	mx_obj_class();
}

MX_SLIDER *mx__hslider(MX_SLIDER_DATA * slider, size_t size, MX_OBJ_DATA * parent, int theid)
{
	MXMINSIZE(size, MX_SLIDER);

	slider = mx__obj(MXOBJ(slider), mx_hslider_class, size, parent, theid);
	if (slider) {
		MXDEBUG_ALLOCTAG(slider);
		MXDEBUG_ATOMNAME(slider, "hslider");
		slider->_min = 5;

		mx_wantpointer(slider, true);
	}
	MXDEBUG_INVARIANT(MXOBJ(slider));
	return (MX_SLIDER *) slider;
}
